﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using Blackbird.Common;
using Blackbird.Common.Exceptions;
using Blackbird.Common.Properties;
using Blackbird.Common.Utility;
using Blackbird.Core.Editable.Base;

namespace Blackbird.Core.Editable.Map
{
    public class EditableMapLayer : Map<BaseEditableMapNode>, IComparable<EditableMapLayer>, IXmlSerializable,
                                    IEditableEntity, IEnumerable<BaseEditableMapNode>
    {
        public EditableMapLayer()
        {
            
        }

        public EditableMapLayer(int width, int height)
        {
            Width = width;
            Height = height;
        }


        private bool _isSelected;
        /// <summary>
        /// Gets the IsSelected property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public bool IsSelected {
            get { return _isSelected; }
            set {
                if (_isSelected == value) {
                    return;
                }
                _isSelected = value;
                InvokePropertyChanged("IsSelected");
            }
        }

        private int _height;
        /// <summary>
        /// Gets the Height property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public int Height {
            get { return _height; }
            set {
                if (_height == value) {
                    return;
                }
                _height = value;
                InvokePropertyChanged("Height");
            }
        }

        private int _width;
        /// <summary>
        /// Gets the Width property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public int Width {
            get { return _width; }
            set {
                if (_width == value) {
                    return;
                }
                _width = value;
                InvokePropertyChanged("Width");
            }
        }

        private int _layerIndex;
        /// <summary>
        /// Gets the LayerIndex property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public int LayerIndex {
            get { return _layerIndex; }
            set {
                if (_layerIndex == value) {
                    return;
                }
                _layerIndex = value;
                InvokePropertyChanged("LayerIndex");
            }
        }

        private double _parallaxAmount;
        /// <summary>
        /// Gets the ParallaxAmount property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public double ParallaxAmount {
            get { return _parallaxAmount; }
            set {
                if (_parallaxAmount == value) {
                    return;
                }
                _parallaxAmount = value;
                InvokePropertyChanged("ParallaxAmount");
            }
        }

        private bool _visible;
        /// <summary>
        /// Gets the Visible property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public bool Visible {
            get { return _visible; }
            set {
                if (_visible == value) {
                    return;
                }
                _visible = value;
                InvokePropertyChanged("Visible");
            }
        }

        #region IComparable<EditableMapLayer> Members

        public int CompareTo(EditableMapLayer other)
        {
            return LayerIndex.CompareTo(other.LayerIndex);
        }

        #endregion

        #region IEditableEntity Members

        public string Name { get; set; }

        public string TypeName
        {
            get { return Resources.EditableMapLayerTypeFriendlyName; }
        }

        public bool IsDirty { get; set; }

        #endregion

        #region IXmlSerializable Members

        public XmlSchema GetSchema()
        {
            throw new NotImplementedException();
        }

        public void ReadXml(XmlReader reader)
        {
            Name = SerializationUtility.ReadAttribute(reader, "name", "name");
            ParallaxAmount = SerializationUtility.ReadAttribute(reader, "parallaxamount", 1.0);
            Width = SerializationUtility.ReadAttribute(reader, "width", 0);
            Height = SerializationUtility.ReadAttribute(reader, "height", 0);
            LayerIndex = SerializationUtility.ReadAttribute(reader, "layerindex", 0);
            Visible = SerializationUtility.ReadAttribute(reader, "visible", true);

            if ((Width < 1) || (Height < 1))
                InvalidDimensionValueException.Throw();

            Initialize();

            reader.ReadStartElement("nodes");
            reader.MoveToContent();
            while (reader.NodeType != XmlNodeType.EndElement)
            {
                reader.ReadStartElement("node");
                string typeName = reader["assemblyqualifiedtypename"];
                if (typeName != null)
                {
                    Type type = Type.GetType(typeName);
                    if (type != null)
                    {
                        var node = (BaseEditableMapNode) Activator.CreateInstance(type);
                        node.ReadXml(reader);
                        this[node.X, node.Y] = node;
                    }
                }
                reader.Read();
            }
        }

        public void WriteXml(XmlWriter writer)
        {
            writer.WriteStartElement("map");
            writer.WriteAttributeString("name", Name);
            writer.WriteAttributeString("parallaxamount", ParallaxAmount.ToString(CultureInfo.CurrentCulture));
            writer.WriteAttributeString("width", Width.ToString(CultureInfo.CurrentCulture));
            writer.WriteAttributeString("height", Height.ToString(CultureInfo.CurrentCulture));
            writer.WriteStartElement("nodes");
            for (int y = 0; y < Height; y++)
            {
                for (int x = 0; x < Width; x++)
                {
                    BaseEditableMapNode node = this[x, y];
                    writer.WriteStartElement("node");
                    node.WriteXml(writer);
                }
            }
            writer.WriteEndElement();
            writer.WriteEndElement();
        }

        #endregion



        public override void Initialize()
        {
            DimensionExtents = new[] {Width, Height};
        }
        
        protected void InvokePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public IEnumerator<BaseEditableMapNode> GetEnumerator()
        {
            return ((IEnumerable<BaseEditableMapNode>) StorageArray).GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }
}